#include "libcomm.h"

static char _BASE64CHAR[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

String CodecBase64::Encode(const void* buffer, int size)
{
	String base64string((size/3 + 1) * 4 + 1); // +1: add '\0' at end.
	char* base64 = (char*)base64string;
	unsigned char* bindata = (unsigned char*)buffer;
	int binlength = size;
	{
		int i, j;
		unsigned char current;

		for (i = 0, j = 0; i < binlength; i += 3)
		{
			current = (bindata[i] >> 2);
			current &= (unsigned char)0x3F;
			base64[j++] = _BASE64CHAR[(int)current];

			current = ((unsigned char)(bindata[i] << 4)) & ((unsigned char)0x30);
			if (i + 1 >= binlength)
			{
				base64[j++] = _BASE64CHAR[(int)current];
				base64[j++] = '=';
				base64[j++] = '=';
				break;
			}
			current |= ((unsigned char)(bindata[i + 1] >> 4)) & ((unsigned char) 0x0F);
			base64[j++] = _BASE64CHAR[(int)current];

			current = ((unsigned char)(bindata[i + 1] << 2)) & ((unsigned char)0x3C);
			if (i + 2 >= binlength)
			{
				base64[j++] = _BASE64CHAR[(int)current];
				base64[j++] = '=';
				break;
			}
			current |= ((unsigned char)(bindata[i + 2] >> 6)) & ((unsigned char) 0x03);
			base64[j++] = _BASE64CHAR[(int)current];

			current = ((unsigned char)bindata[i + 2]) & ((unsigned char)0x3F);
			base64[j++] = _BASE64CHAR[(int)current];
		}
		MYLIB_ASSERT(j < (int)base64string.size());
		base64[j] = '\0';
	}
	return base64string.get();
}

String CodecBase64::Encode(const Buffer& buffer)
{
	return Encode(buffer.get(),buffer.size());
}

Buffer CodecBase64::Decode(const char* base64)
{
	Buffer buffer((strlen(base64) / 4 +1) * 3);
	unsigned char* bindata = (unsigned char*)buffer.get();
	{
		int i, j;
		unsigned char k;
		unsigned char temp[4];
		for (i = 0, j = 0; base64[i] != '\0'; i += 4)
		{
			memset(temp, 0xFF, sizeof(temp));
			for (k = 0; k < 64; k++)
			{
				if (_BASE64CHAR[k] == base64[i])
					temp[0] = k;
			}
			for (k = 0; k < 64; k++)
			{
				if (_BASE64CHAR[k] == base64[i + 1])
					temp[1] = k;
			}
			for (k = 0; k < 64; k++)
			{
				if (_BASE64CHAR[k] == base64[i + 2])
					temp[2] = k;
			}
			for (k = 0; k < 64; k++)
			{
				if (_BASE64CHAR[k] == base64[i + 3])
					temp[3] = k;
			}

			bindata[j++] = ((unsigned char)(((unsigned char)(temp[0] << 2)) & 0xFC)) |
				((unsigned char)((unsigned char)(temp[1] >> 4) & 0x03));
			if (base64[i + 2] == '=')
				break;

			bindata[j++] = ((unsigned char)(((unsigned char)(temp[1] << 4)) & 0xF0)) |
				((unsigned char)((unsigned char)(temp[2] >> 2) & 0x0F));
			if (base64[i + 3] == '=')
				break;

			bindata[j++] = ((unsigned char)(((unsigned char)(temp[2] << 6)) & 0xF0)) |
				((unsigned char)(temp[3] & 0x3F));
		}
		MYLIB_ASSERT(j <= (int)buffer.size());
		buffer.resize(j);
	}
	return buffer;
}